package com.xuan.meihua.core;

import com.nlf.calendar.Lunar;
import com.nlf.calendar.Solar;

import java.util.*;

/**
 * 梅花易数工具
 *
 * @author kty
 */
public class MeiHuaUtil {

    /**
     * ☆判断日期类型并返回农历日期、公历日期
     *
     * @param setting 梅花易数设置
     * @return 日期
     */
    public static Map<String, Object> isDateType(MeiHuaSetting setting) {

        Date date = setting.getDate(); // 日期
        int dateType = setting.getDateType(); // 日期类型（0:公历，1:农历）

        Lunar lunar = null; // 农历日期
        Solar solar = null; // 公历日期
        Map<String, Object> map = new HashMap<>(); // 存放公历日期和农历日期

        // 2、按公历日期计算
        if (dateType == MeiHuaEnum.DATE_TYPE_SOLAR.getMark()) {
            // 2.1、通过公历日期初始化
            lunar = new Lunar(date); // 农历日期
            solar = lunar.getSolar(); // 公历日期
        }

        // 3、按农历日期计算
        if (dateType == MeiHuaEnum.DATE_TYPE_LUNAR.getMark()) {
            // 3.1、通过日期获取年月日时分秒
            Calendar c = Calendar.getInstance();
            c.setTime(date);
            int year = c.get(Calendar.YEAR); // 年
            int month = c.get(Calendar.MONTH) + 1; // 月
            int day = c.get(Calendar.DATE); // 日
            int hour = c.get(Calendar.HOUR_OF_DAY); // 时
            int minute = c.get(Calendar.MINUTE); // 分
            int second = c.get(Calendar.SECOND); // 秒
            // 3.2、通过农历年月日时分秒初始化
            lunar = new Lunar(year, month, day, hour, minute, second); // 农历日期
            solar = lunar.getSolar(); // 公历日期
        }

        // 4、设置日期
        assert null != lunar && null != solar;
        map.put("lunar", lunar); // 设置农历日期
        map.put("solar", lunar.getSolar()); // 设置公历日期
        map.put("lunarStr", lunarStr(lunar)); // 将Lunar型农历日期转为String型农历日期（格式：二〇〇年一月初一子时）
        map.put("solarStr", solarStr(solar)); // 将Solar型公历日期转为String型公历日期（格式：yyyy年M月d日HH时mm分ss秒）

        return map;

    }

    /**
     * ☆判断四柱设置并返回八字
     *
     * @param setting 梅花易数设置
     */
    public static List<String> isSiZhu(MeiHuaSetting setting, Lunar lunar) {

        int yearZhuSet = setting.getYearZhuSet(); // 年柱设置（0:以正月初一作为新年的开始，1：以立春当天作为新年的开始，2：以立春交接的时刻作为新年的开始）
        int monthZhuSet = setting.getMonthZhuSet(); // 月柱设置（0:以节交接当天起算，1：以节交接时刻起算）
        int dayZhuSet = setting.getDayZhuSet(); // 日柱设置（0:晚子时日干支算当天，1：晚子时日干支算明天）
        int hourZhuSet = setting.getHourZhuSet(); // 时柱设置（0:支持早子时和晚子时）

        List<String> list = new ArrayList<>();

        // 1、判断年柱设置
        if (yearZhuSet == MeiHuaEnum.YEAR_ZHU_SET_CHUYI.getMark()) {
            list.add(lunar.getYearInGanZhi()); // 以正月初一作为新年的开始
        } else if (yearZhuSet == MeiHuaEnum.YEAR_ZHU_SET_LICHUN_DAY.getMark()) {
            list.add(lunar.getYearInGanZhiByLiChun()); // 以立春当天作为新年的开始
        } else if (yearZhuSet == MeiHuaEnum.YEAR_ZHU_SET_LICHUN_SHIKE.getMark()) {
            list.add(lunar.getYearInGanZhiExact()); // 以立春交接的时刻作为新年的开始
        } else {
            list.add(lunar.getYearInGanZhi()); // 默认；以正月初一作为新年的开始
        }

        // 2、判断月柱设置
        if (monthZhuSet == MeiHuaEnum.MONTH_ZHU_SET_DAY.getMark()) {
            list.add(lunar.getMonthInGanZhi()); // 以节交接当天起算
        } else if (monthZhuSet == MeiHuaEnum.MONTH_ZHU_SET_SHIKE.getMark()) {
            list.add(lunar.getMonthInGanZhiExact()); // 以节交接时刻起算
        } else {
            list.add(lunar.getMonthInGanZhi()); // 默认；以节交接当天起算
        }

        // 3、判断日柱设置
        if (dayZhuSet == MeiHuaEnum.DAY_ZHU_SET_TOMORROW.getMark()) {
            list.add(lunar.getDayInGanZhiExact()); // 晚子时日柱算明天
        } else if (dayZhuSet == MeiHuaEnum.DAY_ZHU_SET_TODAY.getMark()) {
            list.add(lunar.getDayInGanZhiExact2()); // 晚子时日柱算当天
        } else {
            list.add(lunar.getDayInGanZhiExact()); // 晚子时日柱算明天
        }

        // 4、判断时柱设置
        if (hourZhuSet == MeiHuaEnum.HOUR_ZHU_SET.getMark()) {
            list.add(lunar.getTimeInGanZhi()); // 支持早子时和晚子时
        } else {
            list.add(lunar.getTimeInGanZhi()); // 默认；支持早子时和晚子时
        }

        // 5、返回八字
        return list;

    }

    /**
     * ☆判断起卦模式并返回起卦数
     *
     * @param setting 梅花易数设置
     */
    public static List<Integer> isQiGuaMode(MeiHuaSetting setting) {

        /*
            说明：

                一、数字起卦法：取一个三位数，第一位数字作为上卦数，第二位数字作为下卦数，第一位数字作为动爻数。

                二、单数起卦法：取一个数，前半部分的数字之和÷8取余数作为上卦数，后半部分的数字之和÷8取余数作为下卦数，全数之和÷6作为动爻数。

                三、双数起卦法：
                    1、取第一个数字(和)÷8取余数作为上卦数；
                    2、取第二个数字(和)÷8取余数作为下卦数；
                    3、两个数字之和(+时辰数)÷6作为动爻数。

         */

        List<Integer> list = new ArrayList<>();

        // 1、数字起卦法
        if (setting.getQiGuaMode() == MeiHuaEnum.QI_GUA_MODE_SHU_ZI.getMark()) {
            // 1.1、获取起卦数（起卦模式为[数字]时生效，三位数）
            String number = String.valueOf(setting.getQiGuaNumber());
            // 1.2、添加：上卦数、下卦数、动爻数
            list.add(Integer.parseInt(String.valueOf(number.charAt(0)))); // 上卦数
            list.add(Integer.parseInt(String.valueOf(number.charAt(1)))); // 下卦数
            list.add(Integer.parseInt(String.valueOf(number.charAt(2)))); // 动爻数
        }

        // 2、单数起卦法
        if (setting.getQiGuaMode() == MeiHuaEnum.QI_GUA_MODE_DAN_SHU.getMark()) {
            // 2.1、起卦数（起卦模式为[单数]时生效）
            String number = String.valueOf(setting.getQiGuaDanNumber());
            // 2.2、计算[上卦数、下卦数、动爻数]（单数起卦方式调用）
            list = shangXiaDongNumber(number);
        }

        // 3、双数起卦法
        if (setting.getQiGuaMode() == MeiHuaEnum.QI_GUA_MODE_SHUANG_SHU.getMark()) {
            // 3.1、获取两个数字（起卦模式为[双数]时生效）
            String number1 = String.valueOf(setting.getQiGuaShuangNumber().get(0));
            String number2 = String.valueOf(setting.getQiGuaShuangNumber().get(1));
            // 3.2、计算[上卦数、下卦数、动爻数]（双数起卦方式调用）
            list = shangXiaDongNumber(number1, number2, setting);
        }

        return list;

    }

//------------------------------------------------------------------------------------------------------------------------------------

    /**
     * 将Solar型公历日期转为String型公历日期（转换后格式：yyyy年M月d日HH时mm分ss秒）
     *
     * @param solar 公历日期
     * @return String型公历日期
     */
    private static String solarStr(Solar solar) {

        return (solar.getYear() + "年" + solar.getMonth() + "月" +
                solar.getDay() + "日" + solar.getHour() + "时" +
                solar.getMinute() + "分" + solar.getSecond() + "秒");

    }

    /**
     * 将Lunar型农历日期转为String型农历日期（转换后格式：二〇〇年一月初一子时）
     */
    private static String lunarStr(Lunar lunar) {

        // 获取小时
        int hour = lunar.getHour();

        String lunarStr;

        // 判断时辰
        if (hour >= 23 || hour < 1) {
            // 判断早晚子时
            if (hour >= 23) {
                lunarStr = lunar + "(晚)子时";
            } else {
                lunarStr = lunar + "(早)子时";
            }
        } else {
            lunarStr = lunar + MeiHuaMap.HOUR_ZHI.get(hour) + "时";
        }

        return lunarStr;

    }

    /**
     * 计算[上卦数、下卦数、动爻数]（单数起卦法调用）
     *
     * @param number 一个数
     */
    private static List<Integer> shangXiaDongNumber(String number) {

        int length = number.length(); // 数字位数

        // 1、计算前半段数字之和、后半段数字之和
        int number1Count = 0; // 前半段数字之和
        int number2Count = 0; // 后半段数字之和
        if (length > 1) {
            // 1.1、前半段数字取0~（总位数÷2）位，后半段数字取（总位数÷2）~最后一位
            String number1 = number.substring(0, length / 2);
            String number2 = number.substring(length / 2, length);
            for (int i = 0; i < number1.length(); i++) {
                number1Count += Integer.parseInt(number1.split("")[i]);
            }
            for (int i = 0; i < number2.length(); i++) {
                number2Count += Integer.parseInt(number2.split("")[i]);
            }
        }

        return shangXiaDongNumber(number1Count, number2Count, 0);

    }

    /**
     * 计算[上卦数、下卦数、动爻数]（双数起卦法调用）
     *
     * @param number1 第一个数
     * @param number2 第二个数
     */
    private static List<Integer> shangXiaDongNumber(String number1, String number2, MeiHuaSetting setting) {

        int number1Count = Integer.parseInt(number1); // 第一个数
        int number2Count = Integer.parseInt(number2); // 第二个数

        // 1、双数求和计算上下卦
        if (setting.getQiGuaShuangNumberInSx() == MeiHuaEnum.QI_GUA_MODE_IN_HE_TO_SX.getMark()) {
            number1Count = 0;
            number2Count = 0;
            // 1.1、计算第一个数字的数字之和
            for (int i = 0; i < number1.length(); i++) {
                number1Count += Integer.parseInt(number1.split("")[i]);
            }
            // 1.2、计算第二个数字的数字之和
            for (int i = 0; i < number2.length(); i++) {
                number2Count += Integer.parseInt(number2.split("")[i]);
            }
        }

        // 2、双数求和加时辰数计算动爻
        int hourNumber = number1Count + number2Count;
        if (setting.getQiGuaShuangNumberInDong() == MeiHuaEnum.QI_GUA_MODE_IN_HE_AND_SHI_SHU_TO_DONG.getMark()) {
            // 获取时数
            Lunar lunar = new Lunar(setting.getDate());
            String timeZhi = lunar.getTimeZhi(); // 获取'时地支'
            hourNumber += MeiHuaMap.DI_ZHI_SHU.get(timeZhi); // 获取'时地支'对应的数字并设置【时数】
        }

        return shangXiaDongNumber(number1Count, number2Count, hourNumber);

    }

    /**
     * 通用方法：计算[上卦数、下卦数、动爻数]
     *
     * @param number1 数字1
     * @param number2 数字2
     * @return 上卦数、下卦数、动爻数
     */
    private static List<Integer> shangXiaDongNumber(int number1, int number2, int hourNumber) {

        // 1、计算【上卦数】
        int shangGuaNumber = (number1 % 8);
        shangGuaNumber = (shangGuaNumber == 0 ? 8 : shangGuaNumber); // 若除尽则统一用8来表示

        // 2、计算【下卦数】
        int xiaGuaNumber = (number2 % 8);
        xiaGuaNumber = (xiaGuaNumber == 0 ? 8 : xiaGuaNumber); // 若除尽则统一用8来表示

        // 3、计算【动爻数】
        int dongYaoNumber = ((number1 + number2 + hourNumber) % 6);
        dongYaoNumber = (dongYaoNumber == 0 ? 6 : dongYaoNumber); // 若除尽则统一用6来表示

        return Arrays.asList(shangGuaNumber, xiaGuaNumber, dongYaoNumber);

    }


}
